package com.xinchao.job;

import com.alibaba.fastjson.JSON;
import com.xinchao.cache.RedisCacheDao;
import com.xinchao.constant.CacheConstant;
import com.xinchao.constant.DateConstant;
import com.xinchao.exception.ServiceException;
import com.xinchao.model.dto.AdvertisementMonitorSumDTO;
import com.xinchao.model.vo.DataReportVO;
import com.xinchao.service.AdvertisementMonitorService;
import com.xinchao.service.AdvertisementService;
import com.xinchao.service.DeviceInfoService;
import com.xinchao.util.DateUtils;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 定时任务，计算广告监播数据，将计算出的数据放入redis缓存
 *
 * @author dxy
 * @date 2018/10/22 17:18
 */
@Component
public class CalculateExposuresTouchWatchPlayDurationJob {
	private static Logger logger = LoggerFactory.getLogger(CalculateExposuresTouchWatchPlayDurationJob.class);

	@Autowired
	private AdvertisementMonitorService advertisementMonitorService;
	@Autowired
	private AdvertisementService advertisementService;
	@Autowired
	private RedisCacheDao redisCacheDao;
	@Autowired
	private DeviceInfoService deviceInfoService;

	/**
	 * 每天凌晨30分计算数据
	 */
	@Scheduled(cron = "0 30 0 * * ?")
//	@Scheduled(cron = "0 0/5 * * * ?")
	public void calculate() {
		try {
			//监播表中所有的日期
			List<String> dayList = advertisementMonitorService.listAllDays();
			if (CollectionUtils.isNotEmpty(dayList)) {
				//查询所有的广告名称
				List<String> advertisementNameList = advertisementService.listAllAdvertisementName();
				if (CollectionUtils.isEmpty(advertisementNameList)) {
					logger.info("CalculateExposuresTouchWatchPlayDurationJob 广告名称数据为空 不执行定时任务 ==> ");
					return;
				}
				//所有的点位名称
				List<String> deviceNumberList = deviceInfoService.listAllDeviceNumber();
				if (CollectionUtils.isEmpty(deviceNumberList)) {
					logger.info("CalculateExposuresTouchWatchPlayDurationJob 点位数据为空 不执行定时任务 ==> ");
					return;
				}
				//遍历日期，以日期为缓存key
				for (String day : dayList) {
					//每一天的开始时间
					Long startTime = DateUtils.stringTimeToLongTimeStamp(day + DateConstant.H_M_S_MIN, DateConstant.LONG_DATE_FORMAT_STR);
					//每一天的结束时间
					Long endTime = DateUtils.stringTimeToLongTimeStamp(day + DateConstant.H_M_S_MAX, DateConstant.LONG_DATE_FORMAT_STR);
					//监播缓存（每一天的曝光次数、触达人次、观看人次、观看时长）
					Object monitorCache = redisCacheDao.getCache(CacheConstant.CACHE_NAME_ADVERTISEMENT_MONITOR, day);
					//时段分析缓存（（每一天的每一个时段的曝光次数、触达人次、观看人次、观看时长））
					Object timeFrameAnalysisCache = redisCacheDao.getCache(CacheConstant.CACHE_NAME_TIME_FRAME_ANALYSIS, day);
					if (monitorCache == null && timeFrameAnalysisCache == null) {
						//每一个广告的每一天的数据
						Map<String, AdvertisementMonitorSumDTO> sumMap = new HashMap<>();
						//时段分析
						Map<String, List<DataReportVO>> timeFramMap = new HashMap<>();
						for (String advertisementName : advertisementNameList) {
							AdvertisementMonitorSumDTO dto = advertisementMonitorService.getAdvertisementMonitorSumByTime(advertisementName, startTime, endTime, null);
							if (dto == null) {
								dto = new AdvertisementMonitorSumDTO();
							}
							//每一天每一条广告的曝光次数、触达人次、观看人次、观看时长
							sumMap.put(advertisementName, dto);
							//每一天的时段数据每一条广告的曝光次数、触达人次、观看人次、观看时长
							List<DataReportVO> voList = getTimeFrameAnalisisVoList(day, advertisementName);
							if (CollectionUtils.isNotEmpty(voList)) {
								timeFramMap.put(advertisementName, voList);
							}
						}
						//每一天每一条广告的数据统计缓存
						if (sumMap.size() > 0) {
							redisCacheDao.putCache(CacheConstant.CACHE_NAME_ADVERTISEMENT_MONITOR, day, JSON.toJSONString(sumMap));
						}
						//每一天每一时段每一条广告的数据统计缓存
						if (timeFramMap.size() > 0) {
							redisCacheDao.putCache(CacheConstant.CACHE_NAME_TIME_FRAME_ANALYSIS, day, JSON.toJSONString(timeFramMap));
						}
					}
					//每一天的点位数终端编码缓存
					Object deviceNumberCache = redisCacheDao.getCache(CacheConstant.CACHE_NAME_DEVICE_NUMBER_INDEX, day);
					if (deviceNumberCache == null) {
						//每一天的点位数
						List<String> todayDviceNumberList = advertisementMonitorService.listDeviceNumberByTime(startTime, endTime);
						//点位数缓存
						if (CollectionUtils.isNotEmpty(todayDviceNumberList)) {
							redisCacheDao.putCache(CacheConstant.CACHE_NAME_DEVICE_NUMBER_INDEX, day, JSON.toJSONString(todayDviceNumberList));
						}
					}
					//点位分析
					if (CollectionUtils.isNotEmpty(deviceNumberList)) {
						//点位分析缓存
						Object pointLocationAnalysisCache = redisCacheDao.getCache(CacheConstant.CACHE_NAME_POINT_LOCATION_ANALYSIS, day);
						if (pointLocationAnalysisCache == null) {
							//根据终端编码，广告名称
							Map<String, Map<String, AdvertisementMonitorSumDTO>> pointMap = new HashMap<>();
							for (String deviceNumber : deviceNumberList) {
								Map<String, AdvertisementMonitorSumDTO> deviceNumberMap = new HashMap<>();
								for (String advertisementName : advertisementNameList) {
									AdvertisementMonitorSumDTO dto = advertisementMonitorService.getAdvertisementMonitorSumByTime(advertisementName, startTime, endTime, deviceNumber);
									if (dto == null) {
										continue;
									}
									deviceNumberMap.put(advertisementName, dto);
								}
								if (deviceNumberMap.size() > 0) {
									pointMap.put(deviceNumber, deviceNumberMap);
								}
							}
							if (pointMap.size() > 0) {
								//点位分析缓存
								redisCacheDao.putCache(CacheConstant.CACHE_NAME_POINT_LOCATION_ANALYSIS, day, JSON.toJSONString(pointMap));
							}
						}
					}
				}
			}
		} catch (ParseException e) {
			e.printStackTrace();
			logger.info("CalculateExposuresTouchWatchPlayDurationJob ParseException ==> " + e.getMessage());
		} catch (ServiceException e) {
			e.printStackTrace();
			logger.info("CalculateExposuresTouchWatchPlayDurationJob ServiceException ==> " + e.getMessage());
		}
	}

	/**
	 * 获取时段分析列表
	 *
	 * @param day               日期
	 * @param advertisementName 广告名称
	 * @return List<DataReportVO>
	 * @throws ParseException
	 * @throws ServiceException
	 */
	private List<DataReportVO> getTimeFrameAnalisisVoList(String day, String advertisementName) throws ParseException, ServiceException {
		//返回列表
		List<DataReportVO> voList = new ArrayList<>();
		//解析时间段
		//开始日期
		Long startDate = DateUtils.stringTimeToLongTimeStamp(day, DateConstant.DATE_SHORT_ISO);
		//年月日
		String yearMonthDay = DateUtils.longTimeStampToStringDate(startDate, DateConstant.DATE_SHORT_ISO);
		//时间段列表
		List<String> timeFrameList = DateUtils.generateTimeFrameOfDay(startDate);
		Map<String, String> dayTimeFrameMap = DateConstant.DAY_TIME_FRAME_MAP;
		//结束日期
		Long endDate = null;
		for (int i = 0; i < timeFrameList.size(); i++) {
			String timeFrame = timeFrameList.get(i);
			//完整的结束日期
			endDate = DateUtils.stringTimeToLongTimeStamp(yearMonthDay + " " + timeFrame + ":00", DateConstant.LONG_DATE_FORMAT_STR);
			DataReportVO vo = new DataReportVO();
			//日期
			vo.setTime(timeFrame);
			AdvertisementMonitorSumDTO sumDto = advertisementMonitorService.getAdvertisementMonitorSumByTime(advertisementName, startDate, endDate, null);
			if (sumDto != null) {
				//曝光次数
				Double exposuresSum = sumDto.getExposuresSum();
				vo.setExposuresSum(exposuresSum);
				//触达人次
				Double touchSum = sumDto.getTouchSum();
				vo.setTouchSum(touchSum);
				//观看人次
				Double watchSum = sumDto.getWatchSum();
				vo.setWatchSum(watchSum);
				//观看时长精确到秒
				double duration = 0;
				Double playDurationSum = sumDto.getPlayDurationSum();
				if (playDurationSum == null ) {
					vo.setPlayDurationSum(null);
				} else {
					duration = playDurationSum / 1000;
					vo.setPlayDurationSum(duration);
				}
				//平均观看时长=总时长/观看人次
				if (watchSum == null || watchSum == 0){
					vo.setWatchAvg("");
				} else {
					double watchAvg = duration / watchSum;
					vo.setWatchAvg(String.format("%.2f", watchAvg));
				}
				//观看率=观看人次/触达人次
				if ( watchSum == null || touchSum == null || touchSum == 0) {
					vo.setWatchRatio("");
				} else {
					double watchRatio = watchSum / touchSum;
					String watchRatioFormat = String.format("%.2f", watchRatio);
					//去掉0.
					vo.setWatchRatio(watchRatioFormat.replaceAll("0.", ""));
				}
			}
			voList.add(vo);
			//重新计算开始日期
			String startDateStr = dayTimeFrameMap.get(timeFrame);
			startDate = DateUtils.stringTimeToLongTimeStamp(yearMonthDay + " " + startDateStr, DateConstant.LONG_DATE_FORMAT_STR);
		}
		return voList;
	}
}
